/*************************************************************
 * Title: AccessFilter.java
 * Description: 
 * Author: Huang Shaobin
 * Email: huangshaobin@qtt.cn
 * CreateTime: Feb 11, 2013 4:49:47 PM
 * Copyright © 北京全天通信息咨询服务有限公司 All right reserved
 ************************************************************/
package snow.core.web;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.ArrayUtils;
import org.springframework.core.task.TaskExecutor;
import org.springframework.web.filter.OncePerRequestFilter;

import snow.core.context.AccessContext;
import snow.core.message.MessageSender;

/**
 *
 */
public class AccessFilter extends OncePerRequestFilter {

	private TaskExecutor taskExecutor;
	private MessageSender messageSender;
	private boolean openAccessLog;
	private String characterEncoding = "UTF-8";

	/**
	 * @param request
	 * @param response
	 * @param filterChain
	 * @throws ServletException
	 * @throws IOException
	 * @see org.springframework.web.filter.OncePerRequestFilter#doFilterInternal(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse,
	 *      javax.servlet.FilterChain)
	 */
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
		prepareRequest(request);
		prepareResponse(response);
		try {
			filterChain.doFilter(request, response);
		} finally {
			AccessContext.get().clear();
			AccessContext.remove();
		}
	}

	public MessageSender getMessageSender() {
		return messageSender;
	}

	public TaskExecutor getTaskExecutor() {
		return taskExecutor;
	}

	public boolean isOpenAccessLog() {
		return openAccessLog;
	}

	/**
	 * @param request
	 * @throws UnsupportedEncodingException 
	 */
	protected void prepareRequest(HttpServletRequest request) throws UnsupportedEncodingException {
		request.setCharacterEncoding(characterEncoding);
		AccessContext context = AccessContext.get();
		context.put("request", request);
		prepareRequestLocale(context, request);
		prepareRequestAttribute(context, request);
		prepareRequestHeader(context, request);
		prepareRequestParameter(context, request);
		prepareSession(context, request.getSession());
	}

	protected void prepareRequestAttribute(AccessContext context, HttpServletRequest request) {
		final Enumeration<?> attributeNames = request.getAttributeNames();
		if (attributeNames != null) {
			while (attributeNames.hasMoreElements()) {
				Object element = attributeNames.nextElement();
				if (element instanceof String) {
					final String attributeName = (String) element;
					context.put("request_attribute_" + attributeName, request.getAttribute(attributeName));
				}
			}
		}
	}

	protected void prepareRequestHeader(AccessContext context, HttpServletRequest request) {
		final Enumeration<?> headerNames = request.getHeaderNames();
		if (headerNames != null) {
			while (headerNames.hasMoreElements()) {
				Object element = headerNames.nextElement();
				if (element instanceof String) {
					final String headerName = (String) element;
					final List<Object> headers = new ArrayList<Object>();
					headers.add(request.getHeader(headerName));
					Enumeration<?> enumeration = request.getHeaders(headerName);
					if (enumeration != null) {
						while (enumeration.hasMoreElements()) {
							headers.add(enumeration.nextElement());
						}
					}
					context.put("request_header_" + headerName, headers);
				}
			}
		}
	}

	/**
	 * @param context
	 * @param request
	 */
	protected void prepareRequestLocale(AccessContext context, HttpServletRequest request) {
		List<Locale> locales = new ArrayList<Locale>();
		Locale locale = request.getLocale();
		if (locale != null) {
			locales.add(locale);
		}
		Enumeration<?> enumeration = request.getLocales();
		if (enumeration != null) {
			while (enumeration.hasMoreElements()) {
				Object element = enumeration.nextElement();
				if (element instanceof Locale) {
					locales.add((Locale) element);
				}
			}
		}
		context.put("request_locales", locales);
	}

	/**
	 * @param context
	 * @param request
	 */
	protected void prepareRequestParameter(AccessContext context, HttpServletRequest request) {
		final Enumeration<?> parameterNames = request.getParameterNames();
		if (parameterNames != null) {
			while (parameterNames.hasMoreElements()) {
				Object element = parameterNames.nextElement();
				if (element instanceof String) {
					final String parameterName = (String) element;
					final List<String> values = new ArrayList<String>();
					values.add(request.getParameter(parameterName));
					String[] strings = request.getParameterValues(parameterName);
					if (!ArrayUtils.isEmpty(strings)) {
						for (String string : strings) {
							values.add(string);
						}
					}
					context.put("request_parameter_" + parameterName, values);
				}
			}
		}
	}

	/**
	 * @param response
	 */
	protected void prepareResponse(HttpServletResponse response) {
		response.setCharacterEncoding(characterEncoding);
		AccessContext context = AccessContext.get();
		context.put("response", response);
	}

	/**
	 * @param context
	 * @param session
	 */
	protected void prepareSession(AccessContext context, HttpSession session) {
		context.put("session", session);
		Enumeration<?> attributeNames = session.getAttributeNames();
		if (attributeNames != null) {
			while (attributeNames.hasMoreElements()) {
				Object element = attributeNames.nextElement();
				if (element instanceof String) {
					final String name = (String) element;
					context.put("session_" + name, session.getAttribute(name));
				}
			}
		}
	}

	public void setMessageSender(MessageSender messageSender) {
		this.messageSender = messageSender;
	}

	public void setOpenAccessLog(boolean openAccessLog) {
		this.openAccessLog = openAccessLog;
	}

	public void setTaskExecutor(TaskExecutor taskExecutor) {
		this.taskExecutor = taskExecutor;
	}

	public String getCharacterEncoding() {
		return this.characterEncoding;
	}

	public void setCharacterEncoding(String characterEncoding) {
		this.characterEncoding = characterEncoding;
	}

}
